﻿
#region Usings

using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Media;

#endregion

namespace FasterWPF
{
    #region ColumnContent

    public class ColumnContent<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10> : INotifyPropertyChanged
    {
        public ContentControl Content0 { get; set; }
        public ContentControl Content1 { get; set; }
        public ContentControl Content2 { get; set; }
        public ContentControl Content3 { get; set; }
        public ContentControl Content4 { get; set; }
        public ContentControl Content5 { get; set; }
        public ContentControl Content6 { get; set; }
        public ContentControl Content7 { get; set; }
        public ContentControl Content8 { get; set; }
        public ContentControl Content9 { get; set; }
        public ContentControl Content10 { get; set; }

        public ColumnContent()
        { }

        public void AddContent0<T0>(T0 block0)
    where T0 : FrameworkElement
        {
            Content0 = new ContentControl();
            Content0.Content = block0;
        }

        public void AddContent1<T1>(T1 block1)
            where T1 : FrameworkElement
        {
            Content1 = new ContentControl();
            Content1.Content = block1;
        }

        public void AddContent2<T2>(T2 block2)
    where T2 : FrameworkElement
        {
            Content2 = new ContentControl();
            Content2.Content = block2;
        }

        public void AddContent3<T3>(T3 block3)
where T3 : FrameworkElement
        {
            Content3 = new ContentControl();
            Content3.Content = block3;
        }

        public void AddContent4<T4>(T4 block4)
where T4 : FrameworkElement
        {
            Content4 = new ContentControl();
            Content4.Content = block4;
        }

        public void AddContent5<T5>(T5 block5)
where T5 : FrameworkElement
        {
            Content5 = new ContentControl();
            Content5.Content = block5;
        }

        public void AddContent6<T6>(T6 block6)
where T6 : FrameworkElement
        {
            Content6 = new ContentControl();
            Content6.Content = block6;
        }

        public void AddContent7<T7>(T7 block7)
where T7 : FrameworkElement
        {
            Content7 = new ContentControl();
            Content7.Content = block7;
        }

        public void AddContent8<T8>(T8 block8)
where T8 : FrameworkElement
        {
            Content8 = new ContentControl();
            Content8.Content = block8;
        }

        public void AddContent9<T9>(T9 block9)
where T9 : FrameworkElement
        {
            Content9 = new ContentControl();
            Content9.Content = block9;
        }

        public void AddContent10<T10>(T10 block10)
where T10 : FrameworkElement
        {
            Content10 = new ContentControl();
            Content10.Content = block10;
        }

        //Added to Prevent Memory Leak
        public event PropertyChangedEventHandler PropertyChanged;
    }

    #endregion

    #region ISortArgs

    public class DataGridSort : FasterWPF.CommonExt.ISortArgs
    {
        public DataGridSort(int columnIndexToSortOn)
        {
            ColumnIndexToSortOn = columnIndexToSortOn;
        }

        public int ColumnIndexToSortOn
        {
            get;
            set;
        }

        public CommonExt.SortDataType SortDataType
        {
            get;            
            set;
        }

        public CommonExt.SortDataType SortDataType2
        {
            get;
            set;
        }
    }

    #endregion

    public class DataGridArgs
    {
        public List<string> ColumnHeaderNames
        {
            get;
            set;
        }

        public int Col
        {
            get;
            set;
        }

        public int Row
        {
            get;
            set;
        }

        public string RowGuid
        {
            get;
            set;
        }

        public DataGridArgs(string rowGuid, int row, int col, List<string> columnHeaderNames)
        {
            ColumnHeaderNames = columnHeaderNames;
            Col = col;
            Row = row;
            RowGuid = rowGuid;
        }

        public DataGridArgs(string rowGuid, int row, int col)
        {
            Col = col;
            Row = row;
            RowGuid = rowGuid;
        }
    }
   
    public class DataGridParent : FasterWPF.IParent, ICanSort, FasterWPF.IScrollViewerStrategy
    {         
        //this is ANA?
        public object Generate<T>(object chain, T args, Action<ContentControl> addUIElementsAction)
        {
            return chain;
        }

        #region IParent

        //this is BIND
        public object Add<T>(object chain, string key, T args, Border itemToAdd)
        {
            //key here is the child key

            var obsColl = ((DataGrid)chain).GetSetting<DataGrid, ObservableCollection<ColumnContent<Border, Border, Border, Border, Border, Border, Border, Border, Border, Border, Border>>>("ObsCol");

            //set these here to allow overriding the default values
            ScrollViewerExt.SetCanContentScroll<FrameworkElement>((FrameworkElement)chain);
            //ScrollViewerExt.SetVerticalPageUpButtonDelayAndInterval<FrameworkElement>((FrameworkElement)chain);

            if (args is DataGridArgs)
            {
                DataGridArgs dga = (args as DataGridArgs);

                //Created a new DataGridArgs for EACH child
                DataGridArgs childDga = new DataGridArgs(dga.RowGuid, dga.Row, dga.Col, dga.ColumnHeaderNames);

                //Store the Child DataGridArgs by the child key
                ((DataGrid)chain).StoreSetting<DataGrid>("DataGridPersistArgs_" + key, dga);

                //if columns already added
                if (((DataGrid)chain).Columns != null && ((DataGrid)chain).Columns.Count > 0)
                {
                    //disregard                
                }
                else //otherwise, add the columns
                {
                    if (dga.ColumnHeaderNames != null && dga.ColumnHeaderNames.Count > 0)
                    {
                        int counter = 0;
                        foreach (string colHeader in dga.ColumnHeaderNames)
                        {
                            createDataGridTemplateColumn(((DataGrid)chain), colHeader, counter);
                            counter++;
                        }
                    }
                }

                ColumnContent<Border, Border, Border, Border, Border, Border, Border, Border, Border, Border, Border> cc = null;
                object existingRow = ((DataGrid)chain).GetRow(dga.Row);

                if (existingRow == null)
                {
                    cc = new ColumnContent<Border, Border, Border, Border, Border, Border, Border, Border, Border, Border, Border>();
                    obsColl.Add(cc);
                }
                else
                {
                    if (existingRow is DataGridRow)
                    {
                        cc = (ColumnContent<Border, Border, Border, Border, Border, Border, Border, Border, Border, Border, Border>)((DataGridRow)existingRow).Item;
                    }
                    else
                    {
                        cc = (ColumnContent<Border, Border, Border, Border, Border, Border, Border, Border, Border, Border, Border>)existingRow;                    
                    }
                }

                if (dga.Col == 0)
                {
                    cc.AddContent0<Border>(itemToAdd);
                }
                if (dga.Col == 1)
                {
                    cc.AddContent1<Border>(itemToAdd);
                }
                if (dga.Col == 2)
                {
                    cc.AddContent2<Border>(itemToAdd);
                }
                if (dga.Col == 3)
                {
                    cc.AddContent3<Border>(itemToAdd);
                }
                if (dga.Col == 4)
                {
                    cc.AddContent4<Border>(itemToAdd);
                }
                if (dga.Col == 5)
                {
                    cc.AddContent5<Border>(itemToAdd);
                }
                if (dga.Col == 6)
                {
                    cc.AddContent6<Border>(itemToAdd);
                }
                if (dga.Col == 7)
                {
                    cc.AddContent7<Border>(itemToAdd);
                }
                if (dga.Col == 8)
                {
                    cc.AddContent8<Border>(itemToAdd);
                }
                if (dga.Col == 9)
                {
                    cc.AddContent9<Border>(itemToAdd);
                }
                if (dga.Col == 10)
                {
                    cc.AddContent10<Border>(itemToAdd);
                }
            }

            ((DataGrid)chain).ItemsSource = obsColl;
        
            return chain;
        }

        public void Remove(object chain, string key, Border itemToRemove)
        {
            var obsColl = ((DataGrid)chain).GetSetting<DataGrid, ObservableCollection<ColumnContent<Border, Border, Border, Border, Border, Border, Border, Border, Border, Border, Border>>>("ObsCol");

            DataGridArgs dga = ((DataGrid)chain).GetSetting<DataGrid, DataGridArgs>("DataGridPersistArgs_"+key);

            ColumnContent<Border, Border, Border, Border, Border, Border, Border, Border, Border, Border, Border> cc = null;

            var existingRow = ((DataGridRow)((DataGrid)chain).GetRow(dga.Row));
            cc = (ColumnContent<Border, Border, Border, Border, Border, Border, Border, Border, Border, Border, Border>)existingRow.Item;

            if (dga.Col == 0)
            {
                cc.Content0.Content = null;
                cc.Content0 = null;
            }
            if (dga.Col == 1)
            {
                cc.Content1.Content = null;
                cc.Content1 = null;
            }
            if (dga.Col == 2)
            {
                cc.Content2.Content = null;
                cc.Content2 = null;
            }
            if (dga.Col == 3)
            {
                cc.Content3.Content = null;
                cc.Content3 = null;
            }
            if (dga.Col == 4)
            {
                cc.Content4.Content = null;
                cc.Content4 = null;
            }
            if (dga.Col == 5)
            {
                cc.Content5.Content = null;
                cc.Content5 = null;
            }
            if (dga.Col == 6)
            {
                cc.Content6.Content = null;
                cc.Content6 = null;
            }
            if (dga.Col == 7)
            {
                cc.Content7.Content = null;
                cc.Content7 = null;
            }
            if (dga.Col == 8)
            {
                cc.Content8.Content = null;
                cc.Content8 = null;
            }
            if (dga.Col == 9)
            {
                cc.Content9.Content = null;
                cc.Content9 = null;
            }
            if (dga.Col == 10)
            {
                cc.Content10.Content = null;
                cc.Content10 = null;
            }

            if (existingRow != null)
            {
                if (cc.Content0 == null && cc.Content1 == null && cc.Content2 == null && cc.Content3 == null && cc.Content4 == null && cc.Content5 == null
                    && cc.Content6 == null && cc.Content7 == null && cc.Content8 == null && cc.Content9 == null && cc.Content10 == null)
                {
                    obsColl.Remove(cc); //remove row from DataGrid
                }
            }

            //remove datagridargs for that key
            ((DataGrid)chain).GetSettingsDictionary().Remove("DataGridPersistArgs_" + key);
            
            IComposite ic = (IComposite)(((DataGrid)chain).GetIComposite<DataGrid>());
            ic.RemoveFromChain(itemToRemove);
        }

        public Dictionary<string, object> RetrieveDictionary(object chain)
        {
            return ((DataGrid)chain).GetCompositeLookupDictionary<DataGrid>();
        }
        
        public List<Border> RetrieveCollection(object chain)
        {
            List<Border> lb = new List<Border>();
            foreach (var kvp in ((DataGrid)chain).GetCompositeLookupDictionary<DataGrid>())
            {
                lb.Add((Border)kvp.Value);
            }

            return lb;
        }

        #endregion

        #region ICanSort

        //Clear and Sort Only Occur at the Observable Collection Level. (They do NOT manipulate any WPF Composite Dictionaries.)
        public void ClearCollection(object chain)
        {
            var obsColl = ((DataGrid)chain).GetSetting<DataGrid, ObservableCollection<ColumnContent<Border, Border, Border, Border, Border, Border, Border, Border, Border, Border, Border>>>("ObsCol");
            obsColl.Clear();
        }

        public void AfterSortCollection<T>(object chain)
                where T : FrameworkElement
        {
            //retrieve DataGridArgs of new, sorted rows
            Dictionary<string, DataGridArgs> dgaStore = ((DataGrid)chain).GetSetting<DataGrid, Dictionary<string, DataGridArgs>>("dgaTempStore");
                        
            //get all the other composites (grid cells)
            var cells = ((DataGrid)chain).GetCompositeLookupDictionary();

            //re-assign row number to all the other columns on that Row
            foreach (Border bdr in cells.Values)
            { 
                string strBdr = bdr.GetKey();
                
                //Retrieve and update the Child DataGridArgs
                DataGridArgs digs = ((DataGrid)chain).GetSetting<DataGrid, DataGridArgs>("DataGridPersistArgs_" + strBdr);

                if (dgaStore[digs.RowGuid.ToString()].Col != digs.Col)
                {                     
                    //update other column to reflect the sorted-by column's newly assigned Row number
                    digs.Row = dgaStore[digs.RowGuid.ToString()].Row;
                    Add<DataGridArgs>(chain, strBdr, digs, bdr);
                }
            }

            dgaStore.Clear();
            ((DataGrid)chain).GetSettingsDictionary<DataGrid>().Remove("dgaTempStore");

            CollectionViewSource.GetDefaultView(((ObservableCollection<ColumnContent<Border, Border, Border, Border, Border, Border, Border, Border, Border, Border, Border>>)((DataGrid)chain).ItemsSource)).Refresh();
        }

        private void innerSortCollection(object chain, int row, int dataGridColumnIndexToSortOn, string key, ref int counter, Border item)
        {
           DataGridArgs childdga = ((DataGrid)chain).GetSetting<DataGrid, DataGridArgs>("DataGridPersistArgs_" + key);
              // item.GetKey().ToString());
           Dictionary<string, DataGridArgs> dgaStore = ((DataGrid)chain).GetSetting<DataGrid, Dictionary<string, DataGridArgs>>("dgaTempStore");

           //re-assign row number to that row at the column being sorted on
           if ((childdga.Col == dataGridColumnIndexToSortOn))
           {
               childdga.Row = counter;

               //Add rows
               Add<DataGridArgs>(chain, key, childdga, item);  //key here is the child key

               //store DataGridArgs of rows
               dgaStore.Add(childdga.RowGuid.ToString(), childdga);
           }
           else
           { counter--; }

        }

        public void BeforeSortCollection<T>(object chain, object args, int row, int column, FasterWPF.CommonExt.SortOrder sortOrder, CommonExt.SortControlType sortControlType)
                where T : FrameworkElement
        {
            Dictionary<string, DataGridArgs> dgaStore = new Dictionary<string, DataGridArgs>();

            ((DataGrid)chain).StoreSetting<DataGrid>("dgaTempStore", dgaStore);

            var orderedKeys = ((DataGrid)chain).GetOrderedKeyList();
            var rows = ((DataGrid)chain).GetCompositeLookupDictionary();
            Dictionary<string, object> ChildLookupDict = ((DataGrid)chain).GetChildLookupDictionary();

            IParent parent = ((DataGrid)chain).GetSetting<DataGrid, IParent>("IParent");
            List<Border> dataGridCells = parent.RetrieveCollection(chain);

            if (dataGridCells != null && dataGridCells.Count > 0)
            {             
                ((ICanSort)parent).ClearCollection(chain);
                orderedKeys.Clear();

                IEnumerable<string> items = null;

                if (args is FasterWPF.CommonExt.ISortArgs && (((FasterWPF.CommonExt.ISortArgs)args).SortDataType) == FasterWPF.CommonExt.SortDataType.DateTime)
                {
                    items = CommonInternalExt.PerformSortOfKeysLevelOneDateTime<T>(row, column, sortOrder, ChildLookupDict, items, sortControlType);
                }
                else if (args is FasterWPF.CommonExt.ISortArgs && (((FasterWPF.CommonExt.ISortArgs)args).SortDataType) == FasterWPF.CommonExt.SortDataType.Decimal)
                {
                    items = CommonInternalExt.PerformSortOfKeysLevelOneDecimal<T>(row, column, sortOrder, ChildLookupDict, items, sortControlType);
                }
                else if (args is FasterWPF.CommonExt.ISortArgs && (((FasterWPF.CommonExt.ISortArgs)args).SortDataType) == FasterWPF.CommonExt.SortDataType.Integer)
                {
                    items = CommonInternalExt.PerformSortOfKeysLevelOneInteger<T>(row, column, sortOrder, ChildLookupDict, items, sortControlType);
                }
                else
                {
                    //Default to String
                    items = CommonInternalExt.PerformSortOfKeysLevelOne<T>(row, column, sortOrder, ChildLookupDict, items, sortControlType);
                }
        
                int counter = 0;

                //Add sorted items back to the ListBox
                foreach (string pair in items)
                {     
                    innerSortCollection((chain as DataGrid), 0, ((DataGridSort)args).ColumnIndexToSortOn, pair, ref counter, (Border)rows[pair]);
                    orderedKeys.Add(pair);
                    counter++;
                }
            }   
        }

        public void BeforeSortCollection1Then2<T>(object chain, object args, int row, int column, int row2, int column2, FasterWPF.CommonExt.SortOrder sortOrder, FasterWPF.CommonExt.SortOrder sortOrder2, CommonExt.SortControlType sortControlType)
          where T : FrameworkElement
        {
            Dictionary<string, DataGridArgs> dgaStore = new Dictionary<string, DataGridArgs>();

            ((DataGrid)chain).StoreSetting<DataGrid>("dgaTempStore", dgaStore);

            var orderedKeys = ((DataGrid)chain).GetOrderedKeyList();
            var rows = ((DataGrid)chain).GetCompositeLookupDictionary();

            IParent currentComposite = ((DataGrid)chain).GetSetting<DataGrid, IParent>("IParent");
            List<Border> dataGridCells = currentComposite.RetrieveCollection(chain);

            //if there are items to sort
            if (dataGridCells != null && dataGridCells.Count > 0)
            {
                ((ICanSort)currentComposite).ClearCollection(chain);

                Dictionary<string, Tuple<object, object>> sourceToSortOn = new Dictionary<string, Tuple<object, object>>();

                string defaultNullValue = null;
                string defaultNullValue2 = null;

                if (args is FasterWPF.CommonExt.ISortArgs && (((FasterWPF.CommonExt.ISortArgs)args).SortDataType) == FasterWPF.CommonExt.SortDataType.DateTime)
                {
                    defaultNullValue = "1/1/1900";
                }
                else if (args is FasterWPF.CommonExt.ISortArgs && (((FasterWPF.CommonExt.ISortArgs)args).SortDataType) == FasterWPF.CommonExt.SortDataType.Decimal)
                {
                    defaultNullValue = "0.00";
                }
                else if (args is FasterWPF.CommonExt.ISortArgs && (((FasterWPF.CommonExt.ISortArgs)args).SortDataType) == FasterWPF.CommonExt.SortDataType.Integer)
                {
                    defaultNullValue = "0";
                }
                else
                {
                    defaultNullValue = "";
                }

                if (args is FasterWPF.CommonExt.ISortArgs && (((FasterWPF.CommonExt.ISortArgs)args).SortDataType2) == FasterWPF.CommonExt.SortDataType.DateTime)
                {
                    defaultNullValue2 = "1/1/1900";
                }
                else if (args is FasterWPF.CommonExt.ISortArgs && (((FasterWPF.CommonExt.ISortArgs)args).SortDataType2) == FasterWPF.CommonExt.SortDataType.Decimal)
                {
                    defaultNullValue2 = "0.00";
                }
                else if (args is FasterWPF.CommonExt.ISortArgs && (((FasterWPF.CommonExt.ISortArgs)args).SortDataType2) == FasterWPF.CommonExt.SortDataType.Integer)
                {
                    defaultNullValue2 = "0";
                }
                else
                {
                    defaultNullValue2 = "";
                }

                CommonInternalExt.SetDefaultsForTextElementsAtRowCol<DataGrid>(chain, row, column, row2, column2, sortControlType, orderedKeys, sourceToSortOn, defaultNullValue, defaultNullValue2);
                
                orderedKeys.Clear();
                IOrderedEnumerable<KeyValuePair<string, Tuple<object, object>>> items = null;

                Type dataType1 = null;
                Type dataType2 = null;

                if (args is FasterWPF.CommonExt.ISortArgs && (((FasterWPF.CommonExt.ISortArgs)args).SortDataType) == FasterWPF.CommonExt.SortDataType.DateTime)
                {
                    dataType1 = typeof(DateTime);
                }
                else if (args is FasterWPF.CommonExt.ISortArgs && (((FasterWPF.CommonExt.ISortArgs)args).SortDataType) == FasterWPF.CommonExt.SortDataType.Decimal)
                {
                    dataType1 = typeof(Decimal);
                }
                else if (args is FasterWPF.CommonExt.ISortArgs && (((FasterWPF.CommonExt.ISortArgs)args).SortDataType) == FasterWPF.CommonExt.SortDataType.Integer)
                {
                    dataType1 = typeof(Int32);
                }
                else
                {
                    dataType1 = typeof(String);
                }

                if (args is FasterWPF.CommonExt.ISortArgs && (((FasterWPF.CommonExt.ISortArgs)args).SortDataType2) == FasterWPF.CommonExt.SortDataType.DateTime)
                {
                    dataType2 = typeof(DateTime);
                }
                else if (args is FasterWPF.CommonExt.ISortArgs && (((FasterWPF.CommonExt.ISortArgs)args).SortDataType2) == FasterWPF.CommonExt.SortDataType.Decimal)
                {
                    dataType2 = typeof(Decimal);
                }
                else if (args is FasterWPF.CommonExt.ISortArgs && (((FasterWPF.CommonExt.ISortArgs)args).SortDataType2) == FasterWPF.CommonExt.SortDataType.Integer)
                {
                    dataType2 = typeof(Int32);
                }
                else
                {
                    dataType2 = typeof(String);
                }

                items = CommonInternalExt.PerformSortOfKeysLevelTwo(chain, sortOrder, sortOrder2, sourceToSortOn, items, dataType1, dataType2);
                
                List<string> items2 = new List<string>();
                foreach (KeyValuePair<string, Tuple<object, object>> pair in items)
                {
                    items2.Add(pair.Key);
                }

                int counter = 0;

                //Add sorted items back to the DataGrid
                foreach (string x in items2)
                {
                    innerSortCollection((chain as DataGrid), 0, ((DataGridSort)args).ColumnIndexToSortOn, x, ref counter, (Border)rows[x]);
                    orderedKeys.Add(x);
                    counter++;
                }
            }
        }

        #endregion

        #region IScrollViewerStrategy

        public ScrollViewer GetScrollViewer(object chain)
        {
            DataGrid trv = (DataGrid)chain;
            var builtCheck = trv.ApplyTemplate();
            //  Border bd = (Border)trv.Template.FindName("Bd", trv);
            //base.OnApplyTemplate();

            ScrollViewer scrollViewer = trv.Template.FindName("DG_ScrollViewer", trv) as ScrollViewer;
            var settings = trv.GetSettingsDictionary();

            if (settings.Keys.Contains("ScrollViewerPadding"))
            {
                scrollViewer.Padding = (Thickness)settings["ScrollViewerPadding"];
            }
            else
            {
                scrollViewer.Padding = new Thickness(0); //default to zero
            }

            return scrollViewer;
        }

        public T GetParentContainerFromScrollViewerThumb<T>(System.Windows.Controls.Primitives.Thumb myThumb)
        {
            T source = ((Border)((ScrollViewer)
                       ((Grid)
                       ((ScrollBar)myThumb.TemplatedParent).Parent).TemplatedParent
                       ).Parent).TemplatedParent.Cast<T>();
            return source;
        }

        #endregion

        #region ICanSelect

        public void SetSelected(object chain, Border itemToSetAsSelected)
        {
        }

        #endregion

        //public void GetSelected
        //    return (DataGridRow)grid.ItemContainerGenerator.ContainerFromItem(grid.SelectedItem);

        #region Private Methods

        private static void createDataGridTemplateColumn(DataGrid dg, string columnHeader, int columnNumber)
        {
            DataTemplate ipt = new DataTemplate();

            var txbloc = new FrameworkElementFactory(typeof(ContentControl));

            txbloc.SetBinding(ContentControl.ContentProperty, new Binding("Content" + columnNumber.ToString()));
            ipt.VisualTree = txbloc;

            DataGridTemplateColumn dgtc = new DataGridTemplateColumn();
            dgtc.Header = columnHeader;
            dgtc.CellTemplate = ipt;
            dg.Columns.Add(dgtc);
        }

        #endregion
    }
}
